Designed to model the natural phenomenon of lightning, this comptuer model uses a gradient affected by noise to propagate "lightning" tiles and simulate the spread of lightning through the atmosphere. Darker areas represent areas of higher potential, and lighter areas represent lower potential. The potential gradient is also affected in weight by the height of elevation of the ground in respective areas, giving lightning the tendency to strike higher areas more often.
Try playing around with different sliders and seeing what the collected data can help you infer about the nature of lightning strikes! (You might need to zoom out a little to get a view of the entire project)
x
patches-own [ potential visited? elevation isGround]turtles-own [ path main? life ]globals [ screen-width terrain-noise random-terrain-offset min-pot max-pot column-elevations strike-heights ;; Sliders for controlling the noise strength, branch frequency, and new branch life ;; grain, branches, and branch-life are controlled by sliders]to setup clear-all set screen-width max-pxcor - min-pxcor + 1 set strike-heights[] ;;set terrain-noise (random-float (2 * terrain-noise-strength)) - terrain-noise-strength set random-terrain-offset random-float screen-width setup-potential-field set min-pot min [potential] of patches set max-pot max [potential] of patches ;; experimental code----------------------------------------------------------------- let temp-x min-pxcor repeat screen-width [ randomize-terrain-noise let wave sin(2 * peak-count * temp-x + random-terrain-offset) let temp-y min-pycor let random-elevation sea-level + (terrain-amplitude * wave) + terrain-noise repeat random-elevation [ ask patch temp-x temp-y[ set isGround true set potential max-pot set elevation pycor ] set temp-y (temp-y + 1) ] set temp-x (temp-x + 1) ] set column-elevations n-values screen-width [0] let diffusion-iterator min-pxcor repeat screen-width[ let column-elevation 0 ask patches with [pxcor = diffusion-iterator] [if isGround = true [set column-elevation (column-elevation + 1)]] ask patches with [pxcor = diffusion-iterator] [set potential (potential + column-elevation * column-elevation / 650 + column-elevation / 15)] set column-elevations replace-item (diffusion-iterator + max-pxcor) column-elevations column-elevation set diffusion-iterator (diffusion-iterator + 1) ] repeat 2 [diffuse potential 1.0] ;; Visualize potential gradient reset-atmosphere start-lightning-bolt reset-ticksendto setup-potential-field let noise-strength noise ask patches [ let base (max-pycor - pycor) let temp-noise (ifelse-value (noise-strength = 0) [ 0 ] [ random-float noise-strength - noise-strength / 2 ]) set potential base + temp-noise ]endto reset-atmosphere ask patches [ set visited? false ifelse isGround [ set pcolor 33 ] [ set pcolor scale-color blue potential min-pot max-pot ] ]endto start-lightning-bolt ;; Create main lightning bolt create-turtles 1 [ ;; Set starting position setxy random-float 180 - 90 (max-pycor) set color white set path [] set main? true set life 999 ask patch-here [ set pcolor white set visited? true ] ]endto go if not any? turtles [ reset-atmosphere start-lightning-bolt ] ask turtles [ ;; Branch lifespan if not main? [ set life life - 1 if life <= 0 [ die ] ] ;; Get downward-ish neighbors let neighbors-down neighbors with [ pycor <= [pycor] of myself or pxcor != [pxcor] of myself ] let current-pot [potential] of patch-here let cands neighbors-down with [ potential > current-pot and not visited? ] ;; If grain = 0, prefer directly below if possible let direct-down patch pxcor (pycor - 1) if noise = 0 and direct-down != nobody and not [visited?] of direct-down [ move-to direct-down ] ;; Otherwise, pick the best neighbor (only if grain > 0 or other conditions) ifelse any? cands [ let next ifelse-value (noise = 0) [ max-one-of cands [potential] ] [ max-one-of cands [potential + random-float 1.5] ] face next fd 1 ] [ ;; If no candidates and grain > 0, fallback slightly (only for main) if not any? cands and main? and pycor > min-pycor [ set heading 180 + random 60 - 30 fd 1 ask patch-here [ set pcolor white set visited? true ] ] ] ;; Update patch state ask patch-here [ set pcolor white set visited? true ] ;; Branching logic — disabled when grain = 0 if main? and noise > 0 [ let depth-frac (pycor - min-pycor) / (max-pycor - min-pycor) let branch-chance branches * depth-frac if random-float 1.0 < branch-chance [ hatch 1 [ set color white set path [] set main? false set life branch-life ;; Use branch-life slider to determine branch life rt random 120 - 60 fd 1 ask patch-here [ set pcolor white set visited? true ] ] ] ] ;; Die if done or touching ground if [elevation] of patch-here != 0 or (not main? and not any? cands) [ if main? [ set strike-heights lput (pycor + 90) strike-heights set-current-plot "Lightning Strikes" set-current-plot-pen "strike-height" plot ycor + 90 set-current-plot-pen "average-strike-height" plot mean strike-heights ] die ] ] tickendto randomize-terrain-noise set terrain-noise (random-float (2 * terrain-noise-strength)) - terrain-noise-strengthendto reset-simulation clear-all setupend